//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_UTILITY_UVALUE_H_
#define _BUOLA_UTILITY_UVALUE_H_

#include <buola/buola.h>
#include <buola/traits/typename.h>

namespace buola {

/// \addtogroup utility
///@{

DECLARE_NAME(FromString);

////////////////////////////////////////////////////////////////////////////

/// contains any value represented as a string

/// It provides a mechanism for serialising simple data types
///
////////////////////////////////////////////////////////////////////////////

class UValue
{
public:
///\name constructors
//@{
    ///create an empty value
    UValue()
    {}

    ///initialize parsing a string
    UValue(NFromString,const std::string &pString)
    {
        auto lBar=pString.find('|');
        if(lBar!=std::string::npos)
        {
            mType=pString.substr(0,lBar);
            mValue=pString.substr(lBar+1);
        }
    }

    ///initialize from type and value
    UValue(const std::string &pType,const std::string &pValue)
        :   mType(pType)
        ,   mValue(pValue)
    {}

    ///copy constructor
    UValue(const UValue &pO)
        :   mType(pO.mType)
        ,   mValue(pO.mValue)
    {}

    ///move constructor
    UValue(UValue &&pO)
        :   mType(std::move(pO.mType))
        ,   mValue(std::move(pO.mValue))
    {}

    ///construct from a value of any type
    template<typename tType>
    explicit UValue(const tType &pValue)
        :   mType(MTypeName<tType>::Get())
        ,   mValue(to_string(pValue))
    {}
//@}

    ///returns true if it contains no value
    bool empty() const
    {
        return mType.empty();
    }

    ///returns the type as a string
    const std::string &Type() const   {   return mType;   }
    ///returns the value as a string
    const std::string &Value() const  {   return mValue;  }
    ///returns the type and the value as a string
    std::string ToString() const      {   return mType+'|'+mValue;    }

    ///swaps the content of two values
    void swap(UValue &pRH)
    {
        std::swap(mType,pRH.mType);
        std::swap(mValue,pRH.mValue);
    }

    ///assign a value of any type
    template<typename tType>
    UValue &operator=(const tType &pRH)
    {
        UValue(pRH).swap(*this);
        return *this;
    }

    ///copy assignment operator
    UValue &operator=(const UValue &pRH)
    {
        mType=pRH.mType;
        mValue=pRH.mValue;
        return *this;
    }

    ///move assignment operator
    UValue &operator=(UValue&& pRH)
    {
        swap(pRH);
        return *this;
    }

    ///assign an empty value
    void clear()
    {
        mType.clear();
        mValue.clear();
    }

    ///returns true if the content is of type \p tType
    template<typename tType>
    bool HasType() const
    {
        return (mType==MTypeName<tType>::Get());
    }

    ///get the content, which must be of type tType

    ///\throws XInvalid if there is no value or it is of another type
    ///\returns a reference to the content
    template<typename tType>
    tType Get() const
    {
        if(!HasType<tType>())
            throw XInvalid("bad cast in UValue::Get");
        return to_<tType>(mValue);
    }

private:
    std::string mType;
    std::string mValue;
};

template<typename tSerializer>
void serialize(tSerializer &pS,const UValue &pV)
{
    pS << pV.Type() << pV.Value();
}

template<typename tSerializer>
void deserialize(tSerializer &pS,UValue &pV)
{
    std::string lType,lValue;
    
    pS >> lType >> lValue;
    pV=UValue(lType,lValue);
}

///@}

/*namespace buola*/ }

#endif
